/**
 *    Copyright 2009-2018 the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package org.apache.ibatis.type;

import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

import org.apache.ibatis.executor.result.ResultMapException;
import org.apache.ibatis.session.Configuration;

/**
 * The base {@link TypeHandler} for references a generic type.
 * <p>
 * Important: Since 3.5.0, This class never call the {@link ResultSet#wasNull()}
 * and {@link CallableStatement#wasNull()} method for handling the SQL
 * {@code NULL} value. In other words, {@code null} value handling should be
 * performed on subclass.
 * </p>
 * 
 * @author Clinton Begin
 * @author Simone Tripodi
 * @author Kzuki Shimizu
 */
public abstract class BaseTypeHandler<T> extends TypeReference<T> implements TypeHandler<T> {

	/**
	 * @deprecated Since 3.5.0 - See
	 *             https://github.com/mybatis/mybatis-3/issues/1203. This field will
	 *             remove future.
	 */
	@Deprecated
	protected Configuration configuration;

	/**
	 * @deprecated Since 3.5.0 - See
	 *             https://github.com/mybatis/mybatis-3/issues/1203. This property
	 *             will remove future.
	 */
	@Deprecated
	public void setConfiguration(Configuration c) {
		this.configuration = c;
	}

	@Override
	public void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException {
		if (parameter == null) {
			if (jdbcType == null) {
				throw new TypeException(
						"JDBC requires that the JdbcType must be specified for all nullable parameters.");
			}
			try {
				// 绑定参数为null的处理
				ps.setNull(i, jdbcType.TYPE_CODE);
			} catch (SQLException e) {
				throw new TypeException("Error setting null for parameter #" + i + " with JdbcType " + jdbcType + " . "
						+ "Try setting a different JdbcType for this parameter or a different jdbcTypeForNull configuration property. "
						+ "Cause: " + e, e);
			}
		} else {
			try {
				// 绑定非空参数，该方法抽象方法，由子类实现（通过PreparedStatement的API去设置非空参数）
				setNonNullParameter(ps, i, parameter, jdbcType);
			} catch (Exception e) {
				throw new TypeException("Error setting non null for parameter #" + i + " with JdbcType " + jdbcType
						+ " . "
						+ "Try setting a different JdbcType for this parameter or a different configuration property. "
						+ "Cause: " + e, e);
			}
		}
	}

	@Override
	public T getResult(ResultSet rs, String columnName) throws SQLException {
		try {
			// 抽象方法，有多个重载，由子类实现
			return getNullableResult(rs, columnName);
		} catch (Exception e) {
			throw new ResultMapException(
					"Error attempting to get column '" + columnName + "' from result set.  Cause: " + e, e);
		}
	}

	@Override
	public T getResult(ResultSet rs, int columnIndex) throws SQLException {
		try {
			return getNullableResult(rs, columnIndex);
		} catch (Exception e) {
			throw new ResultMapException(
					"Error attempting to get column #" + columnIndex + " from result set.  Cause: " + e, e);
		}
	}

	@Override
	public T getResult(CallableStatement cs, int columnIndex) throws SQLException {
		try {
			return getNullableResult(cs, columnIndex);
		} catch (Exception e) {
			throw new ResultMapException(
					"Error attempting to get column #" + columnIndex + " from callable statement.  Cause: " + e, e);
		}
	}

	/**
	 * 绑定非空参数，该方法抽象方法，由子类实现
	 * <pre>
	 *     通过PreparedStatement的API去设置非空参数
	 * </pre>
	 * @param ps PreparedStatement
	 * @param i 下标号
	 * @param parameter 参数
	 * @param jdbcType 参数类型
	 * @throws SQLException Sql异常
	 */
	public abstract void setNonNullParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType)
			throws SQLException;

	public abstract T getNullableResult(ResultSet rs, String columnName) throws SQLException;

	public abstract T getNullableResult(ResultSet rs, int columnIndex) throws SQLException;

	public abstract T getNullableResult(CallableStatement cs, int columnIndex) throws SQLException;

}
